﻿using System;
using System.Collections.Generic;
using AllInOnePluginBL.Rules;
using NLog;
using PluginDB;

namespace AllInOnePluginBL.Helpers
{
    /// <summary>
    /// Summary description for PluginDBHandler
    /// </summary>
    public sealed class PluginDBHandler
    {
        private readonly Logger _log = LogManager.GetLogger("PluginDBHandler");
        private readonly DBManager _dbManager;
        private readonly List<IRule> _moviesRules = new List<IRule>(); 
        //private readonly object _lock_object = new object();

        #region Ctor
        PluginDBHandler()
        {
            this._dbManager = new DBManager();
            this._moviesRules.Add(new ExtractSeriesNameRule());
            this._moviesRules.Add(new VerifyBrokenLinkRule());
            this._moviesRules.Add(new ExtractMovieInfoRule());
        }

        public static PluginDBHandler Instance
        {
            get
            {
                return Nested.instance;
            }
        }

        class Nested
        {
            // Explicit static constructor to tell C# compiler
            // not to mark type as beforefieldinit
            static Nested()
            {
            }

            internal static readonly PluginDBHandler instance = new PluginDBHandler();
        }
        #endregion

        #region Plugin Videos Methods
        internal List<Category> GetCategoriesList(int page, int pageSize)
        {
            return _dbManager.GetCategoriesList(page, pageSize);
        }

        internal List<ValidVideosView> GetLatestMoviesList(int page, int pageSize)
        {
            return _dbManager.GetLatestMoviesList(page, pageSize);
        }

        internal List<ValidTVShowsView> GetLatestTVShowsList(int page, int pageSize)
        {
            return _dbManager.GetLatestTVShowsList(page, pageSize);
        }

        internal List<ValidVideosView> GetHebSubMovies(int page, int pageSize)
        {
            return _dbManager.GetHebSubMovies(page, pageSize);
        }

        internal List<ValidSeriesView> GetHebSubTVShows(int page, int pageSize)
        {
            return _dbManager.GetHebSubTVShows(page, pageSize);
        }

        internal bool IsVideoExists(string pluginUrl)
        {
            var exists = _dbManager.IsTVShowExists(pluginUrl);
            if(!exists)
                exists = _dbManager.IsMovieExists(pluginUrl);

            return exists;
        }

        internal bool IsSeriesExists(string seriesName)
        {
            return _dbManager.IsSeriesExists(seriesName);
        }

        internal void AddNewVideo(PluginVideoInfo pluginVideoInfo)
        {
            if (IsVideoExists(pluginVideoInfo.PluginUrl)) return;

            ApplyVideoRules(pluginVideoInfo);

            if (pluginVideoInfo.Links.Count == 0)
            {
                _log.Warn("{0} has no valid links. not saving it to DB", pluginVideoInfo.VideoName);
                return;
            }
            _dbManager.AddVideo(pluginVideoInfo);
            
            _log.Debug("Movie {1} Saved At {0}", pluginVideoInfo.VideoName, DateTime.Now);
        }

        internal List<Site> GetSitesNames(int page, int pageSize)
        {
            return _dbManager.GetSitesNames(page, pageSize);
        }

        internal List<ValidVideosView> GetMoviesByCategoryId(long categoryId, int page, int pageSize)
        {
            return _dbManager.GetMoviesByCategoryId(categoryId, page, pageSize);
        }

        internal List<ValidTVShowsView> GetTVShowsByCategoryId(long categoryId, int page, int pageSize)
        {
            return _dbManager.GetTVShowsByCategoryId(categoryId, page, pageSize);
        }

        internal List<ValidVideosView> GetHebDubMovies(int page, int pageSize)
        {
            return _dbManager.GetHebDubMovies(page, pageSize);
        }

        internal List<ValidVideosView> GetAtoZMoviesList(string startWord, bool isHebWord, int page, int pageSize)
        {
            return _dbManager.GetAtoZMoviesList(startWord, isHebWord, page, pageSize);
        }

        internal List<ValidSeriesView> GetAtoZSeriesList(string startWord, bool isHebWord, int page, int pageSize)
        {
            return _dbManager.GetAtoZSeriesList(startWord, isHebWord, page, pageSize);
        }

        internal List<ValidVideosView> GetMoviesBySite(long siteId, int page, int pageSize)
        {
            return _dbManager.GetMoviesBySite(siteId, page, pageSize);
        }

        internal List<ValidSeriesView> GetTVShowsBySite(long siteId, int page, int pageSize)
        {
            return _dbManager.GetTVShowsBySite(siteId, page, pageSize);
        }

        internal Video GetMovieByID(long videoId)
        {
            return _dbManager.GetMovieByID(videoId);
        }

        internal TVShow GetTVShowByID(long videoId)
        {
            return _dbManager.GetTVShowByID(videoId);
        }

        internal VideosLink GetVideoLinkByID(long linkId)
        {
            return _dbManager.GetVideoLinkByID(linkId);
        }

        internal TVShowsLink GetTVShowLinkByID(long linkId)
        {
            return _dbManager.GetTVShowLinkByID(linkId);
        }

        internal List<ValidSeriesView> GetSeriesList(int page, int pageSize)
        {
            return _dbManager.GetSeriesList(page, pageSize);
        }

        internal List<ValidTVShowsView> GetTVShowsBySeries(long seriesId, int page, int pageSize)
        {
            return _dbManager.GetTVShowsBySeries(seriesId, page, pageSize);
        }

        internal List<long> GetHebSubSeasonsbySeriesId(long seriesId, int page, int pageSize)
        {
            return _dbManager.GetHebSubSeasonsbySeriesId(seriesId, page, pageSize);
        }

        internal List<long> GetSeasonsbySeriesAndSiteId(long siteId, long seriesId, int page, int pageSize)
        {
            return _dbManager.GetSeasonsbySeriesAndSiteId(siteId, seriesId, page, pageSize);
        }

        internal List<long> GetSeasonsbySeriesId(long seriesId, int page, int pageSize)
        {
            return _dbManager.GetSeasonsbySeriesId(seriesId, page, pageSize);
        }

        internal List<ValidTVShowsView> GetHebSubTVShowsBySeasonId(long seriesId, long seasonNumber, int page, int pageSize)
        {
            return _dbManager.GetHebSubTVShowsBySeasonId(seriesId, seasonNumber, page, pageSize);
        }

        internal List<ValidTVShowsView> GetTVShowsBySeasonAndSiteId(long siteId, long seriesId, long seasonNumber, int page, int pageSize)
        {
            return _dbManager.GetTVShowsBySeasonAndSiteId(siteId, seriesId, seasonNumber, page, pageSize);
        }

        internal List<ValidTVShowsView> GetTVShowsBySeasonId(long seriesId, long seasonNumber, int page, int pageSize)
        {
            return _dbManager.GetTVShowsBySeasonId(seriesId, seasonNumber, page, pageSize);
        }

        internal FileHostingCooky GetCookieCode(string sourceName)
        {
            return _dbManager.GetCookieCode(sourceName);
        }

        internal void InsertNewCookieCode(string cookieCode, DateTime expiredDate, string sourceName)
        {
            _dbManager.InsertNewCookieCode(cookieCode, expiredDate, sourceName);
        }

        internal List<ValidVideosView> GetMoviesBySearchText(string searchText, int page, int pageSize)
        {
            return _dbManager.GetMoviesBySearchText(searchText, page, pageSize);
        }

        internal List<ValidSeriesView> GetTVShowsBySearchText(string searchText, int page, int pageSize)
        {
            return _dbManager.GetTVShowsBySearchText(searchText, page, pageSize);
        }

        internal List<TVShow> GetAllTVShows()
        {
            return _dbManager.GetAllTVShows();
        }

        internal List<Video> GetAllVideos()
        {
            return _dbManager.GetAllVideos();
        }

        internal List<VideosLink> GetAllVideosLinks(bool getOnlyInvalid)
        {
            return _dbManager.GetAllVideosLinks(getOnlyInvalid);
        }

        internal List<TVShowsLink> GetAllTVShowsLinks()
        {
            return _dbManager.GetAllTVShowsLinks();
        }

        internal void SaveAllVideos()
        {
            _dbManager.SaveAllVideos();
        }

        internal void SaveAllTVShows()
        {
            _dbManager.SaveAllTVShows();
        }

        public List<Video> GetMoviesBySite(long siteId)
        {
            return _dbManager.GetAllVideosBySite(siteId);
        }
        
        public bool IsUserSeriesExists(long userId, long seriesId)
        {
            return _dbManager.IsUserSeriesExists(userId, seriesId);
        }

        public void AddSeriesToUser(long userId, Series series)
        {
            _dbManager.AddSeriesToUser(userId, series);
        }

        public bool IsUserExists(long userId)
        {
            return _dbManager.IsUserExists(userId);
        }

        public bool IsUserNameExists(string userName)
        {
            return _dbManager.IsUserNameExists(userName);
        }

        public bool IsUserExists(string userUniqeGuid)
        {
            return _dbManager.IsUserExists(userUniqeGuid);
        }

        public bool IsUserExists(string userName, string password)
        {
            return _dbManager.IsUserExists(userName, password);
        }

        public User GetUser(long userId)
        {
            return _dbManager.GetUser(userId);
        }

        public User GetUser(string userName, string password)
        {
            return _dbManager.GetUser(userName, password);
        }

        public User GetUser(string userGuid)
        {
            return _dbManager.GetUser(userGuid);
        }

        public User CreateUser(string userName, string password)
        {
            return _dbManager.CreateUser(userName, password);
        }

        internal List<Series> GetSeriesByUserId(long userId)
        {
            return _dbManager.GetSeriesByUserId(userId);
        }

        internal Series GetSeries(long seriesId)
        {
            return _dbManager.GetSeries(seriesId);
        }

        internal bool IsSeriesExists(long seriesId)
        {
            return _dbManager.IsSeriesExists(seriesId);
        }

        internal List<ValidSeriesView> GetUserSeriesList(string userName, string password)
        {
            var user = GetUser(userName, password);
            if (user == null) throw new Exception("User doesnt exists");

            var userTvShows = GetSeriesByUserId(user.UserID);
            var series = new List<ValidSeriesView>();

            foreach (var userTvShow in userTvShows)
            {
                series.AddRange(_dbManager.GetSeriesBySeriesId(userTvShow.SeriesID));
            }
            return series;
        }

        internal void RemoveSeriesForUser(long userId, long seriesId)
        {
            _dbManager.RemoveSeriesForUser(userId, seriesId);
        }

        internal bool IsUserVideoExists(long userId, long videoId)
        {
            return _dbManager.IsUserVideoExists(userId, videoId);
        }

        internal void AddVideoToUser(long userId, Video video)
        {
            _dbManager.AddVideoToUser(userId, video);
        }

        internal void RemoveVideoForUser(long userId, Video video)
        {
            _dbManager.RemoveVideoForUser(userId, video);
        }

        public long GetSeriesIdFromVideoId(int videoId)
        {
            return _dbManager.GetSeriesIdFromVideoId(videoId);
        }

        internal List<ValidVideosView> GetUserVideoList(string userName, string password)
        {
            var user = GetUser(userName, password);
            if (user == null) throw new Exception("User doesnt exists");

            var userVideos = _dbManager.GetMovieLinksByUserId(user.UserID);
            var videos = new List<ValidVideosView>();

            foreach (var userVideo in userVideos)
            {
                videos.AddRange(_dbManager.GetVideosByVideoId(userVideo.VideoID));
            }
            return videos;
        } 
        #endregion

        /// <summary>
        /// Merge duplicate series links
        /// </summary>
        public void MergeSeriesNames()
        {
            var seriesList = _dbManager.GetAllSeries();
            foreach (var series in seriesList)
            {
                var seriesItems = _dbManager.GetSeriesByName(series.Name);
                var dbSeries = _dbManager.GetSeriesById(series.SeriesID);
                if (dbSeries == null) continue;

                foreach (var item in seriesItems)
                {
                    if (item.SeriesID == series.SeriesID) continue;
                    var tvShows = _dbManager.GetTVShowsBySeriesId(item.SeriesID);
                    foreach (var tvShow in tvShows)
                    {
                        tvShow.Series = series;
                    }
                    if (string.IsNullOrEmpty(series.Plot) &&
                        !string.IsNullOrEmpty(item.Plot))
                    {
                        series.Plot = item.Plot;
                    }
                    _dbManager.DeleteSeries(item);

                    _dbManager.SaveAllSeries();
                }
            }
        }

        internal void MergeMoviesLinks()
        {
            var moviesList = _dbManager.GetAllVideos();
            foreach (var movie in moviesList)
            {
                var origVideoItem = _dbManager.GetMovieByID(movie.VideoID);
                var videosbyName = _dbManager.GetMoviesByName(movie.VideoName);
                if (origVideoItem == null) continue;

                foreach (var videoItem in videosbyName)
                {
                    if (videoItem.VideoID == movie.VideoID) continue;

                    var videoLinks = _dbManager.GetMovieLinksByVideoID(videoItem.VideoID);
                    foreach (var videoLink in videoLinks)
                    {
                        foreach (var videosLink in movie.VideosLinks)
                        {
                            if (!videosLink.LinkURL.Equals(videoLink.LinkURL) &&
                                videosLink.IsValid.HasValue &&
                                videosLink.IsValid.Value)
                            {
                                movie.VideosLinks.Add(new VideosLink
                                {
                                    LinkURL = videoLink.LinkURL,
                                    IsValid = videoLink.IsValid,
                                    Source = videoLink.Source,
                                    ValidDate = videoLink.ValidDate,
                                    Video = movie
                                });
                            }
                            _dbManager.DeleteVideoLink(videosLink);
                        }
                    }

                    _log.Debug("Delete movie: {0} - {1}", videoItem.VideoID, videoItem.VideoName);
                    _dbManager.DeleteVideo(videoItem);

                    _dbManager.SaveAllVideos();
                }
            }

        }

        private void ApplyVideoRules(PluginVideoInfo pPluginVideoInfo)
        {
            foreach (var rule in _moviesRules)
            {
                rule.ApplyRule(pPluginVideoInfo);
            }
        }

        internal List<JobsType> GetJobsTypesList()
        {
            return _dbManager.GetJobsTypesList();
        }

        internal List<Job> GetSiteScrapperJobs()
        {
            return _dbManager.GetSiteScrapperJobs();
        }

        internal void UpdateJobTypeRunTime(long jobTypeID, DateTime runTime)
        {
            _dbManager.UpdateJobTypeRunTime(jobTypeID, runTime);
        }

        internal void UpdateJobRunTime(long jobId, DateTime runTime)
        {
            _dbManager.UpdateJobRunTime(jobId, runTime);
        }

        internal Job GetJob(string jobName)
        {
            return _dbManager.GetJob(jobName);
        }

        internal Job CreateJob(string jobName, string jobTypeName)
        {
            return _dbManager.CreateJob(jobName, jobTypeName);
        }
        
        internal JobsType CreateJobType(string jobName, long runtimeIntervalInHours)
        {
            return _dbManager.CreateJobType(jobName, runtimeIntervalInHours);
        }

        internal JobsType GetJobType(string jobName)
        {
            return _dbManager.GetJobType(jobName);
        }

        public bool ActivateUser(string userGuid)
        {
            return _dbManager.ActivateUser(userGuid);
        }

        internal bool IsSiteExists(string siteName)
        {
            return _dbManager.IsSiteExists(siteName);
        }

        internal int GetVideosCount()
        {
            return _dbManager.GetVideosCount();
        }

        internal int GetSeriesCount()
        {
            return _dbManager.GetSeriesCount();
        }

        internal int GetUserVideosCount(long userID)
        {
            return _dbManager.GetUserVideosCount(userID);
        }

        internal int GetUserSeriesCount(long userID)
        {
            return _dbManager.GetUserSeriesCount(userID);
        }

        internal List<VideosLink> GetVideosLinksBySourceID(long sourceId)
        {
            return _dbManager.GetVideosLinksBySourceID(sourceId);
        }

        internal List<TVShowsLink> GetTVShowsLinksBySourceID(long sourceId)
        {
            return _dbManager.GetTVShowsLinksBySourceID(sourceId);
        }

        internal void DeleteVideo(Video video)
        {
            _dbManager.DeleteVideo(video);
        }

        internal void DeleteVideoLink(VideosLink videosLink)
        {
            _dbManager.DeleteVideoLink(videosLink);
        }

        internal void DeleteTVShow(TVShow tVShow)
        {
            _dbManager.DeleteTVShow(tVShow);
        }

        internal void DeleteTVShowLink(TVShowsLink videoLink)
        {
            _dbManager.DeleteTVShowLink(videoLink); ;
        }
    }
}